Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
@salesforce/ts-types
Advanced tools
@salesforce/ts-types is a TypeScript utility library that provides a set of type definitions and utilities to help with type-safe programming in TypeScript. It is particularly useful for Salesforce developers who need to work with complex types and ensure type safety in their applications.
Optional
The `Optional` type allows you to define a type that can either be a specific type or `undefined`. This is useful for handling optional parameters or properties in a type-safe manner.
import { Optional } from '@salesforce/ts-types';
function getLength(str: Optional<string>): number {
return str ? str.length : 0;
}
console.log(getLength('Hello')); // 5
console.log(getLength(undefined)); // 0
Dictionary
The `Dictionary` type is a utility type that represents an object with string keys and values of a specified type. This is useful for creating objects that act as maps or dictionaries.
import { Dictionary } from '@salesforce/ts-types';
const userRoles: Dictionary<string> = {
'admin': 'Administrator',
'user': 'Regular User'
};
console.log(userRoles['admin']); // 'Administrator'
Nullable
The `Nullable` type allows you to define a type that can either be a specific type or `null`. This is useful for handling nullable values in a type-safe manner.
import { Nullable } from '@salesforce/ts-types';
function greet(name: Nullable<string>): string {
return name ? `Hello, ${name}!` : 'Hello!';
}
console.log(greet('Alice')); // 'Hello, Alice!'
console.log(greet(null)); // 'Hello!'
The `utility-types` package provides a set of utility types for TypeScript, similar to @salesforce/ts-types. It includes types like `Optional`, `Nullable`, and `Dictionary`, among others. It is a more general-purpose library and is not specifically tailored for Salesforce development.
The `type-fest` package is a collection of essential TypeScript types. It offers a wide range of utility types that can help with type-safe programming. While it provides similar functionalities to @salesforce/ts-types, it is more extensive and not specifically focused on Salesforce.
The `ts-essentials` package provides essential TypeScript types and utilities. It includes types for handling optional and nullable values, as well as other utility types. It is similar to @salesforce/ts-types but is designed for general TypeScript development rather than being Salesforce-specific.
This is a simple TypeScript-oriented library developed for use in Salesforce TypeScript libraries, applications, and plugins consisting of two parts:
See the API documentation for more details on each of the utilities that ts-types
provides.
We were interested in developing with strict compiler settings in TypeScript. Among the sub-settings that comprise strict mode are strictNullChecks
, strictPropertyInitialization
, and noImplicitAny
. tslint
provides additional rules that further improve code quality and type correctness by discouraging explicit any
usages and other unsafe constructs such as some classes of unwarranted type assertions. Together, these settings have the potential to increase code quality substantially, reducing the frequency of certain classes of runtime errors typical in classical JavaScript applications. They also encourage the writing of clearer, more accurately typed code, which helps teams work together more effectively and more rapidly onboard new hires.
Of course, stricter compiler settings require developers to write more type-safe code -- or to work around the compiler's insistence on type-safety. Often this stricter style leads to more verbose code in the way of type declarations and type guards, and can require new and occasionally unfamiliar patterns to accomplish without subverting the compiler's enforcement of the type system (typically via the use of type assertions).
TypeScript provides both syntax and built-in types designed to help write well-typed code, but we can be more terse and concise by employing additional types and type-narrowing utility functions by leveraging language features like type predicates backed by sound runtime validation, simplified undefined
and null
checking, etc. That's where this library comes in.
This library has its roots in solving the problem of how to handle untyped JSON data in a type-safe way. It was born when we added some basic type declarations to replace the unsafe any
type as a stand-in for JSON data with a type that could capture the range of data available in any JSON construct. This yielded the AnyJson
type, which is effectively a union of all primitive and collection JSON values. The type alone was not enough to make for convenient, type-guarded handling of JSON data, however. TypeScript supports a very elegant system of control flow analysis that will narrow the type of a variable in code after the compiler can prove the set of possible types of the variable has been reduced. Using type guards in your code improves its runtime type safety characteristics, makes it more readable, and provides richer typing information for IDEs. Type guards are implemented as conditional statements, however, and can quickly become noisy and make what was once terse JavaScript code expand into several lines of type checking. This library aimed to simplify the experience of reducing the amount of type guards needed to process a typed-JSON data structure by providing several convenience functions that help extract well-typed data from such JSON structures.
For example, look at the following typical untyped JSON processing in JavaScript:
// concise, but not at all null-safe or type-safe; often made to be at least null-safe using lodash fns
JSON.parse(response.body).results.forEach(item => db.save(item.id, item));
Then a safe version in bare TypeScript using type guards:
const json = JSON.parse(response.body);
// type of json -> `any`, but will not be undefined or JSON.parse would throw
if (json === null && typeof json !== 'object') throw new Error('Unexpected json data type');
let results = json.results;
// type of results -> `any`
if (!Array.isArray(results)) results = [];
// type of results -> `any[]`
results.forEach(item => {
// type of item -> `any`
const id = item.id;
// type of id -> `any`
if (typeof id !== 'string') throw new Error('Unexpected item id data type');
// type of id -> `string`
db.save(id, item);
});
While that's pretty safe, it's also a mess to read and write. That's why this library is here to help!
const json = ensureJsonMap(JSON.parse(response.body));
// type of json -> `JsonMap` or raises an error
const results = asJsonArray(json.results, []);
// type of results -> `JsonArray` or uses the default of `[]`
results.forEach(item => {
// type of item -> `AnyJson`
record = ensureJsonMap(record);
db.save(ensureString(record.id), record);
});
Removing the comments, we can shorten the above somewhat to achieve something not much more complex than the original example, but with robust type and null checking implemented:
asJsonArray(ensureJsonMap(JSON.parse(response.body)).results, []).forEach(item => {
const record = ensureJsonMap(item);
db.save(ensureString(record.id), record);
});
The ensure*
functions are used in this example since they will raise an error when the value being checked either does not exist or does not match the expected type. Additionally, and perhaps more importantly, the generic any
and AnyJson
types get progressively narrowed when using these functions to more specific types. Of course, you don't always want to raise an error when these conditions are not met, so alternative forms exist for each of the JSON data types that allow the types to be tested and narrowed -- see the is*
and as*
variants in the API documentation for testing and narrowing capabilities without additionally raising errors.
After a few iterations of working on the JSON support types and utilities, it became apparent that we needed other non-JSON types and functions that provide similar capabilities. Rather than create a new library for those, we instead grew the scope of this one to contain all of our commonly used types and narrowing functions.
A small library of types is included to help write more concise TypeScript code. These types are in part designed to augment the standard types included with the TypeScript library. Please see the generated API documentation for the complete set of provided types. Here are a few of the most commonly used types:
T | undefined
.undefined
from a type T
, when T
includes undefined
as a union member.Optional<T | null>
, or T | null | undefined
. NonNullable
is a TypeScript built-in that subtracts both null
and undefined
from a type T
with either as a union member.string
-indexed object
of the form { [key: string]: Optional<T> }
.Extract<keyof T, string>
.JsonPrimitive | JsonCollection
.null | string | number| boolean
.Dictionary<AnyJson>
.Array<AnyJson>
.JsonMap | JsonArray
.This library provides several categories of functions to help with safely narrowing variables of broadly typed variables, like unknown
or object
, to more specific types.
The is*
suite of functions accept a variable of a broad type such as unknown
or object
and returns a boolean
type-predicate useful for narrowing the type in conditional scopes.
// type of value -> string | boolean
if (isString(value)) {
// type of value -> string
}
// type of value -> boolean
The as*
suite of functions accept a variable of a broad type such as unknown
or object
and optionally returns a narrowed type after validating it with a runtime test. If the test is negative or if the value was not defined (i.e. undefined
or null
), undefined
is returned instead.
// some function that takes a string or undefined
function upperFirst(s: Optional<string>): Optional<string> {
return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
}
// type of value -> unknown
const name = upperFirst(asString(value));
// type of name -> Optional<string>
The ensure*
suite of functions narrow values' types to a definite value of the designated type, or raises an error if the value is undefined
or of an incompatible type.
// type of value -> unknown
try {
const s = ensureString(value);
// type of s -> string
} catch (err) {
// s was undefined, null, or not of type string
}
The has*
suite of functions both tests for the existence and type-compatibility of a given value and, if the runtime value check succeeds, narrows the type to a view of the original value's type intersected with the tested property (e.g. T & { [_ in K]: V }
where K
is the test property key and V
is the test property value type).
// type of value -> unknown
if (hasString(value, 'name')) {
// type of value -> { name: string }
// value can be further narrowed with additional checks
if (hasArray(value, 'results')) {
// type of value -> { name: string } & { results: unknown[] }
} else if (hasInstance(value, 'error', Error)) {
// type of value -> { name: string } & { error: Error }
}
}
The get*
suite of functions search an unknown
target value for a given path. Search paths follow the same syntax as lodash
's get
, set
, at
, etc. These functions are more strictly typed, however, increasingly the likelihood that well-typed code stays well-typed as a function's control flow advances.
// imagine response json retrieved from a remote query
const response = {
start: 0,
length: 2,
results: [{ name: 'first' }, { name: 'second' }]
};
const nameOfFirst = getString(response, 'results[0].name');
// type of nameOfFirst = string
The coerce
suite of functions accept values of general types and narrow their types to JSON-specific values. They are named with the coerce
prefix to indicate that they do not perform an exhaustive runtime check of the entire data structure -- only shallow type checks are performed. As a result, only use these functions when you are confident that the broadly typed subject being coerced was derived from a JSON-compatible value. If you are unsure of an object's origins or contents but want to avoid runtime errors handling elements, see the to*
set of functions.
const response = coerceJsonMap(JSON.parse(await http.get('http://example.com/data.json').body));
// type of response -> JsonMap
The to*
suite of functions is a fully type-safe version of the coerce*
functions for JSON narrowing, but at the expense of some runtime performance. Under the hood, the to*
functions perform a JSON-clone of their subject arguments, ensuring that the entire data structure is JSON compatible before returning the narrowed type.
const obj = {
name: 'example',
parse: function(s) {
return s.split(':');
}
};
const json = toJsonMap(obj);
// type of json -> JsonMap
// json = { name: 'example' }
// notice that the parse function has been omitted to ensure JSON-compatibility!
This suite of functions are used to iterate the keys, entries, and values of objects with some typing conveniences applied that are not present in their built-in counterparts (i.e. Object.keys
, Object.entries
, and Object.values
), but come with some caveats noted in their documentation. Typical uses include iterating over the properties of an object with more useful keyof
typings applied during the iterator bodies, and/or filtering out undefined
or null
values before invoking the iterator functions.
const pets: Dictionary<string> = {
fido: 'dog',
bill: 'cat',
fred: undefined
};
// note that the array is typed as [string, string] rather than [string, string | undefined]
function logPet([name, type]: [string, string]) {
console.log('%s is a %s', name, type);
}
definiteEntriesOf(pets).forEach(logPet);
// fido is a dog
// bill is a cat
Another Salesforce TypeScript library, @salesforce/kit, builds on this library to add additional utilities. It includes additional JSON support, a lightweight replacement for some lodash
functions, and growing support for patterns used in other Salesforce CLI libraries and applications.
FAQs
Types and related utilities for TypeScript
We found that @salesforce/ts-types demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 47 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.